import java.io.IOException;
import java.io.Reader;

public class SceneParser extends Parser{
	private Scene scene;
	private Camera camera;
	private DirectionalLight directionalLight;
	private PointLight pointLight;
	private AreaLight areaLight;
	private Surface surface;
	private Rectangle rectangle;
	private Disc disc;
	private Sphere sphere;
	private Box box;
	private Cylinder cylinder;
	
	public SceneParser() {
		this.scene = new Scene();
	}


	public void startFile() {}

	public void endFile() throws ParseException {}

	// start a new object definition
	// return true if recognized
	public boolean addObject(String ObjName) throws ParseException
	{
		this.setObj(ObjName);
		
		//Scene:
		if (ObjName.equals("scene")) {
		}
		
		//Camera:
		else if (ObjName.equals("camera")) {
			this.camera = new Camera();
		}
		
		//Lights:
		else if (ObjName.equals("light-directed")) {
			this.directionalLight = new DirectionalLight();
		}
		
		else if (ObjName.equals("light-point")) {
			this.pointLight = new PointLight();
		}
		
		else if (ObjName.equals("light-area")) {
			this.areaLight = new AreaLight();
		}
		
		//Primitives:
		
		else if (ObjName.equals("rectangle")) {
			
			this.surface = new Surface();
			this.rectangle = new Rectangle();
			
		}
		
		else if (ObjName.equals("disc")) {
			
			this.disc = new Disc();
			this.surface = new Surface();
		}
		
		else if (ObjName.equals("sphere")) 
		{
			
			this.sphere = new Sphere();
			this.surface = new Surface();
		}

		else if (ObjName.equals("box")) 
		{
			
			this.box = new Box();
			this.surface = new Surface();
		}

		else if (ObjName.equals("cylinder")) 
		{
			
			this.cylinder = new Cylinder();
			this.surface = new Surface();
		}

		else {
			this.reportError("Unidentified object: " + ObjName);
		}

	
		return true;
	}
	
	// set a specific parameter for the current object
	// return true if recognized
	
	public boolean setParameter(String Attribute, String[] Values) throws ParseException
	{
		this.setAttribute(Attribute);
		this.setValues(Values);
		String Obj = this.getObj();
		
		//Scene:
		if (Obj.equals("scene")) {
			if (Attribute.equals("background-col")) {
				RGB bgColor = new RGB(Double.parseDouble(Values[0]), Double.parseDouble(Values[1]) , Double.parseDouble(Values[2])  );
				this.scene.setBackgroundCol(bgColor);
			}
			
			else if (Attribute.equals("background-tex")) {
				this.scene.setBckgroundTex(Values[0]);
			}
			else if (Attribute.equals("ambient-light")) {
				RGB ambientColor = new RGB(Double.parseDouble(Values[0]) , Double.parseDouble(Values[1]) , Double.parseDouble(Values[2])  );
				this.scene.setAmbientLight(ambientColor);
			}
			else if (Attribute.equals("super-samp-width")) {
				this.scene.setSuperSampWidth(Integer.parseInt(Values[0]));
			}
			else if (Attribute.equals("use-acceleration")) {
				this.scene.setUseAcceleration(Boolean.parseBoolean(Values[0]));  
			}				
		}
		
		//Camera:
		else if (Obj.equals("camera")) {
			if (Attribute.equals("eye")){
				Vector eye = new Vector(Double.parseDouble(Values[0]), Double.parseDouble(Values[1]), Double.parseDouble(Values[2]) );
				this.camera.setEye(eye);
			}
			else if (Attribute.equals("direction")){
				Vector direction = new Vector(Double.parseDouble(Values[0]), Double.parseDouble(Values[1]), Double.parseDouble(Values[2]) );
				this.camera.setDirection(direction);
			}
			else if (Attribute.equals("look-at")){
				Vector lookAt = new Vector(Double.parseDouble(Values[0]), Double.parseDouble(Values[1]), Double.parseDouble(Values[2]));
				Vector direction = Vector.sub(lookAt, this.camera.getEye());
				this.camera.setDirection(direction.normlized());
			}
			
			else if (Attribute.equals("up-direction")){
				Vector up = new Vector(Double.parseDouble(Values[0]), Double.parseDouble(Values[1]), Double.parseDouble(Values[2]) );
				if (Vector.dotProduct(this.camera.getDirection(), up)==0.0) {
					this.camera.setUp(up);
				}
				else {
					Vector right = Vector.crossProduct(this.camera.getDirection(), up);
					this.camera.setUp(Vector.crossProduct(right, this.camera.getDirection()));
				}
				
			}
			
			else if (Attribute.equals("screen-dist")){
				double distance = Double.parseDouble(Values[0]);
				this.camera.setScreenDist(distance);
			}
			
			else if (Attribute.equals("screen-width")){
				double screenWidth = Double.parseDouble(Values[0]);
				this.camera.setScreenWidth(screenWidth);
			}
			
		}

		//Lights:
		else if (Obj.equals("light-directed")) {
			if (Attribute.equals("color")){
				RGB color = new RGB(Double.parseDouble(Values[0]) , Double.parseDouble(Values[1]) , Double.parseDouble(Values[2])  );
				this.directionalLight.setColor(color);
			}
			else if (Attribute.equals("direction")){
				Vector direction = new Vector(Double.parseDouble(Values[0]), Double.parseDouble(Values[1]), Double.parseDouble(Values[2]) );
				this.directionalLight.setDirection(direction);
			}
		}
		
		else if (Obj.equals("light-point")) {
			if (Attribute.equals("color")){
				RGB color = new RGB(Double.parseDouble(Values[0]) , Double.parseDouble(Values[1]) , Double.parseDouble(Values[2])  );
				this.pointLight.setColor(color);
			}
			else if (Attribute.equals("pos")){
				Vector pos = new Vector(Double.parseDouble(Values[0]), Double.parseDouble(Values[1]), Double.parseDouble(Values[2]) );
				this.pointLight.setPos(pos);
			}
			else if (Attribute.equals("attenuation")) {
				this.pointLight.setAttenuation(Double.parseDouble(Values[0]), Double.parseDouble(Values[1]), Double.parseDouble(Values[2]) );
				
			}
		}
		
		else if (Obj.equals("light-area")) {
			if (Attribute.equals("color")){
				RGB color = new RGB(Double.parseDouble(Values[0]) , Double.parseDouble(Values[1]) , Double.parseDouble(Values[2])  );
				this.areaLight.setColor(color);
			}
			
			else if (Attribute.equals("p0")){
				
				Vector p0 = new Vector(Double.parseDouble(Values[0]), Double.parseDouble(Values[1]), Double.parseDouble(Values[2]));
				this.areaLight.setP0(p0);
			}
			
			else if (Attribute.equals("p1")){
				
				Vector p1 = new Vector(Double.parseDouble(Values[0]), Double.parseDouble(Values[1]), Double.parseDouble(Values[2]));
				this.areaLight.setP1(p1);
			}
			
			else if (Attribute.equals("p2")){
				
				Vector p2 = new Vector(Double.parseDouble(Values[0]), Double.parseDouble(Values[1]), Double.parseDouble(Values[2]));
				this.areaLight.setP2(p2);
			}
			
		}

		//Primitives:
		
		else if (Obj .equals("rectangle")) {
			
			if (Attribute.equals("p0")){
				
				Vector p0 = new Vector(Double.parseDouble(Values[0]), Double.parseDouble(Values[1]), Double.parseDouble(Values[2]));
				this.rectangle.setP0(p0);
			}
			
			else if (Attribute.equals("p1")){
				
				Vector p1 = new Vector(Double.parseDouble(Values[0]), Double.parseDouble(Values[1]), Double.parseDouble(Values[2]));
				this.rectangle.setP1(p1);
			}
			
			else if (Attribute.equals("p2")){
				
				Vector p2 = new Vector(Double.parseDouble(Values[0]), Double.parseDouble(Values[1]), Double.parseDouble(Values[2]));
				this.rectangle.setP2(p2);
			}
			
		}
		
		else if (Obj .equals("disc")) {
			if (Attribute.equals("center")) {
				Vector center = new Vector(Double.parseDouble(Values[0]), Double.parseDouble(Values[1]), Double.parseDouble(Values[2]));
				this.disc.setCenter(center);
			}

			else if (Attribute.equals("normal")) {
				Vector normal = new Vector(Double.parseDouble(Values[0]), Double.parseDouble(Values[1]), Double.parseDouble(Values[2]));
				this.disc.setNormal(normal);
			}

			else if (Attribute.equals("radius")) {
				this.disc.setRadius(Double.parseDouble(Values[0]));
				
			}

		}
		
		else if (Obj .equals("sphere")) {
			if (Attribute.equals("center")) {
				Vector center = new Vector(Double.parseDouble(Values[0]), Double.parseDouble(Values[1]), Double.parseDouble(Values[2]));
				this.sphere.setCenter(center);
			}

			else if (Attribute.equals("radius")) {
				this.sphere.setRadius(Double.parseDouble(Values[0]));
				
			}
	
		}

		else if (Obj .equals("box")) {
			if (Attribute.equals("p0")){
				
				Vector p0 = new Vector(Double.parseDouble(Values[0]), Double.parseDouble(Values[1]), Double.parseDouble(Values[2]));
				this.box.setP0(p0);
			}
			
			else if (Attribute.equals("p1")){
				
				Vector p1 = new Vector(Double.parseDouble(Values[0]), Double.parseDouble(Values[1]), Double.parseDouble(Values[2]));
				this.box.setP1(p1);
			}
			
			else if (Attribute.equals("p2")){
				
				Vector p2 = new Vector(Double.parseDouble(Values[0]), Double.parseDouble(Values[1]), Double.parseDouble(Values[2]));
				this.box.setP2(p2);
			}
			else if (Attribute.equals("p3")){
				
				Vector p3 = new Vector(Double.parseDouble(Values[0]), Double.parseDouble(Values[1]), Double.parseDouble(Values[2]));
				this.box.setP3(p3);
			}

		}

		else if (Obj .equals("cylinder")) {
			if (Attribute.equals("start")) {
				Vector start = new Vector(Double.parseDouble(Values[0]), Double.parseDouble(Values[1]), Double.parseDouble(Values[2]));
				this.cylinder.setStart(start);
			}

			else if (Attribute.equals("direction")) {
				Vector direction = new Vector(Double.parseDouble(Values[0]), Double.parseDouble(Values[1]), Double.parseDouble(Values[2]));
				this.cylinder.setDirection(direction);
			}
			
			else if (Attribute.equals("length")) {
				this.cylinder.setLength(Double.parseDouble(Values[0]));
			}
			
			else if (Attribute.equals("radius")) {
				this.cylinder.setRadius(Double.parseDouble(Values[0]));
			}
			

		}

		//Update surface attributes for any primitive:
		if ((Obj.equals("rectangle"))|(Obj.equals("disc"))|(Obj.equals("sphere"))|(Obj.equals("box"))|(Obj.equals("cylinder"))) {

			if (Attribute.equals("mtl-type")) {
				this.surface.setMtlType(Values[0]);
			}
			
			else if (Attribute.equals("texture")) {
				this.surface.setTexture(Values[0]);
			}
			//Kd
			else if (Attribute.equals("mtl-diffuse")) {
				RGB mtlDiffuse = new RGB(Double.parseDouble(Values[0]) , Double.parseDouble(Values[1]) , Double.parseDouble(Values[2])  );
				this.surface.setMtlDiffuse(mtlDiffuse);
			}
			//Ks
			else if (Attribute.equals("mtl-specular")) {
				RGB mtlSpecular = new RGB(Double.parseDouble(Values[0]) , Double.parseDouble(Values[1]) , Double.parseDouble(Values[2])  );
				this.surface.setMtlSpecular(mtlSpecular);
			}
			//Ka
			else if (Attribute.equals("mtl-ambient")) {
				RGB mtlAmbient = new RGB(Double.parseDouble(Values[0]) , Double.parseDouble(Values[1]) , Double.parseDouble(Values[2])  );
				this.surface.setMtlAmbient(mtlAmbient);
			}
			//Ie
			else if (Attribute.equals("mtl-emission")) {
				RGB mtlEmission = new RGB(Double.parseDouble(Values[0]) , Double.parseDouble(Values[1]) , Double.parseDouble(Values[2])  );
				this.surface.setMtlEmission(mtlEmission);
			}
			
			else if (Attribute.equals("checkers-diffuse1")) {
				RGB checkersDiffuse1 = new RGB(Double.parseDouble(Values[0]) , Double.parseDouble(Values[1]) , Double.parseDouble(Values[2])  );
				this.surface.setCheckersDiffuse1(checkersDiffuse1);
			}
			
			else if (Attribute.equals("checkers-diffuse2")) {
				RGB checkersDiffuse2 = new RGB(Double.parseDouble(Values[0]) , Double.parseDouble(Values[1]) , Double.parseDouble(Values[2])  );
				this.surface.setCheckersDiffuse2(checkersDiffuse2);
			}
			
			else if (Attribute.equals("checkers-size")) {
				this.surface.setCheckersSize(Double.parseDouble(Values[0]));
			}
			
			else if (Attribute.equals("mtl-shininess")) {
				this.surface.setMtlShininess(Double.parseDouble(Values[0]));
			}
			
			else if (Attribute.equals("reflectance")) {
				this.surface.setReflectance(Double.parseDouble(Values[0]));
			}
		}
		
		return true;
	}
	
	// finish the parsing of the current object
	// here is the place to perform any validations over the parameters and
	// final initializations.
	public void commit() throws ParseException
	{
		String Obj = this.getObj();
		
		//Scene:
		if (Obj.equals("scene")) {
						
		}
		
		//Camera:
		else if (Obj.equals("camera")) {
			this.scene.setCamera(this.camera);
		}
		
		else if (Obj.equals("light-directed")) {
			this.scene.addLight(this.directionalLight);
		}
		
		else if (Obj.equals("light-point")) {
			this.scene.addLight(this.pointLight);
			
		}
		
		else if (Obj.equals("light-area")) {
			this.scene.addLight(this.areaLight);
		}
		
		//Primitives:
		else if (Obj.equals("rectangle")) {
			this.rectangle.setSurface(this.surface);
			this.scene.addPrimitive(this.rectangle);
		}
		
		else if (Obj.equals("disc")) {
			this.disc.setSurface(this.surface);
			this.scene.addPrimitive(this.disc);
		}
		
		else if (Obj.equals("sphere")) {
			this.sphere.setSurface(this.surface);
			this.scene.addPrimitive(this.sphere);
		}
		
		else if (Obj.equals("box")) {
			this.box.setSurface(this.surface);
			this.scene.addPrimitive(this.box);
		}
		
		else if (Obj.equals("cylinder")) {
			this.cylinder.setSurface(this.surface);
			this.scene.addPrimitive(this.cylinder);
		}
		
		else {
			this.reportError("Unidentified object: " + Obj);
		}
		
		this.delObj();
		this.delAttribute();
		this.delValues();
		
	}
	
	public Scene getParsedScene() {
		return this.scene;
	}
	
	public void reportError(String err)
	{
		System.out.println("ERROR: " + err);
	}

}
